/*
 * // +-------------------------------------------------------------------------------------------------
 * // |                 有你就好 [ 有节骨乃坚，无心品自端 ]     <http://encoding.wang>
 * // +-------------------------------------------------------------------------------------------------
 * // |                             独在异乡为异客         每逢佳节倍思亲
 * // +-------------------------------------------------------------------------------------------------
 * // |                 联系:   <707069100@qq.com>      <http://weibo.com/513778937>
 * // +-------------------------------------------------------------------------------------------------
 */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                   ErYang出品 属于小极品          共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------


package wang.encoding.mroot.admin.common.shiro

import org.apache.commons.lang3.builder.ReflectionToStringBuilder
import org.apache.commons.lang3.builder.ToStringStyle
import org.apache.shiro.SecurityUtils
import org.apache.shiro.authc.*
import org.apache.shiro.authz.AuthorizationException
import org.apache.shiro.authz.AuthorizationInfo
import org.apache.shiro.authz.SimpleAuthorizationInfo
import org.apache.shiro.subject.PrincipalCollection
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Component
import wang.encoding.mroot.admin.common.constant.ConfigConst
import wang.encoding.mroot.admin.common.util.ShiroSessionUtil
import wang.encoding.mroot.common.business.ResultData
import wang.encoding.mroot.common.config.DigestManageConfiguration
import wang.encoding.mroot.model.entity.system.Role
import wang.encoding.mroot.model.entity.system.Rule
import wang.encoding.mroot.model.entity.system.User
import wang.encoding.mroot.model.enums.StatusEnum
import wang.encoding.mroot.service.system.RoleService
import wang.encoding.mroot.service.system.RuleService
import wang.encoding.mroot.service.system.UserService
import java.math.BigInteger
import java.util.*


/**
 * 为当前登录的 Subject 授予角色和权限
 *
 * @author ErYang
 */
@Component
class ShiroDbRealm : OperatorAuthorizingRealmWithRpn() {


    companion object {

        private val logger: Logger = LoggerFactory.getLogger(ShiroDbRealm::class.java)
    }

    // -------------------------------------------------------------------------------------------------

    /* 项目上整合 shiro 安全框架，要使用 延时加载 @Lazy 不然缓存 Cache 不能正常使用 */
    /* shiro 框架与 spring 框架的 BeanFactory 有所冲突 导致注入 shiro 框架的类不能被 spring 正确初始化 */


    @Autowired
    @Lazy
    private lateinit var userService: UserService

    @Autowired
    @Lazy
    private lateinit var roleService: RoleService

    @Autowired
    @Lazy
    private lateinit var ruleService: RuleService

    @Autowired
    private lateinit var digestManageConfiguration: DigestManageConfiguration

    @Autowired
    private lateinit var configProperties: ConfigConst

    /**
     * 验证当前登录的 Subject 登录时调用
     *
     * @param authenticationToken AuthenticationToken
     * @return AuthenticationInfo
     * @throws AuthenticationException
     */
    @Throws(AuthenticationException::class)
    override fun doGetAuthenticationInfo(authenticationToken: AuthenticationToken): AuthenticationInfo? {

        val usernamePasswordToken: UsernamePasswordToken = authenticationToken as UsernamePasswordToken
        if (logger.isDebugEnabled) {
            logger.debug(">>>>>>>>验证当前Subject时获取到usernamePasswordToken为:"
                    + ReflectionToStringBuilder.toString(usernamePasswordToken,
                    ToStringStyle.MULTI_LINE_STYLE) + "<<<<<<<<")
        }

        val username: String = usernamePasswordToken.username.trim().toLowerCase()
        val password: String = digestManageConfiguration.getSha512(String(usernamePasswordToken.password).trim())

        val user: User? = userService.getByUsernameAndPassword(username, password)

        if (null != user) {

            // 调用业务逻辑方法来处理登录请求
            val result: ResultData = userService.login(username, password)
            if (result.isOk!!) {
                if (StatusEnum.DELETE.key == user.status) {
                    logger.error(">>>>>用户[" + user.username + "]已被删除<<<<<")
                    throw LockedAccountException()
                }

                if (StatusEnum.DISABLE.key == user.status) {
                    logger.error(">>>>>用户[" + user.username + "]已被禁用<<<<<")
                    throw LockedAccountException()
                }
                // 登录信息放入 session 中
                ShiroSessionUtil.setAttribute(configProperties.adminSessionName, userService.aseDecryptData(user))
                ShiroSessionUtil.setAttribute(configProperties.adminSessionIdName, ShiroSessionUtil.getSessionId())
                return SimpleAuthenticationInfo(user.username, user.password, user.username)
            }

        }
        return null
    }

    // -------------------------------------------------------------------------------------------------

    /**
     * 为当前登录的 Subject 授予角色和权限
     * 1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”)：
     *  自己去调用这个是否有什么角色或者是否有什么权限的时候；
     * 2、@RequiresRoles("admin") ：在方法上加注解的时候；
     * 3、[@shiro.hasPermission name = "admin"][/@shiro.hasPermission]：
     * 在页面上加shiro标签的时候，即进这个页面的时候扫描到有这个标签的时候
     *
     * @param principalCollection principalCollection
     * @return AuthorizationInfo
     */
    override fun doGetAuthorizationInfo(principalCollection: PrincipalCollection): AuthorizationInfo {
        // 非正常退出，即没有显式调用 SecurityUtils.getSubject().logout()
        // (可能是关闭浏览器，或超时)，但此时缓存依旧存在(principals)，所以会自己跑到授权方法里
        if (!SecurityUtils.getSubject().isAuthenticated) {
            doClearCache(principalCollection)
            SecurityUtils.getSubject().logout()
            if (logger.isErrorEnabled) {
                logger.error(">>>>>Shiro授权错误<<<<<")
            }
            throw AuthorizationException()
        }

        val simpleAuthorInfo = SimpleAuthorizationInfo()

        // 获取当前登陆的用户 等价于(String)principals.fromRealm(this.getName()).iterator().next()
        val currentUsername: String = super.getAvailablePrincipal(principalCollection) as String
        // 根据用户名获取用户信息
        val user: User? = userService.getByUsername(currentUsername)

        if (null != user) {
            // 角色
            val stringRoles = ArrayList<String>()
            // 权限
            val stringRules = ArrayList<String>()
            // 所属角色
            val roles: Set<Role> = roleService.listByUserId(user.id!!)!!
            var rules: TreeSet<Rule>
            if (roles.isNotEmpty()) {
                for (role: Role in roles) {
                    // 添加角色
                    stringRoles.add(role.name!!)
                    // 所属角色拥有的权限
                    // 添加权限
                    rules = ruleService.listByRoleId(role.id!!)!!
                    for (rule: Rule in rules) {
                        if (BigInteger.ZERO != rule.pid) {
                            stringRules.add(rule.url!!)
                        }
                    }
                }
                // 返回权限
                simpleAuthorInfo.addRoles(stringRoles)
                simpleAuthorInfo.addStringPermissions(stringRules)
            }
        }
        return simpleAuthorInfo
    }

    // -------------------------------------------------------------------------------------------------

}

// -----------------------------------------------------------------------------------------------------

// End ShiroDbRealm class

/* End of file ShiroDbRealm.kt */
/* Location: ./src/main/kotlin/wang/encoding/mroot/admin/common/shiro/ShiroDbRealm.kt */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                           ErYang出品 属于小极品  O(∩_∩)O~~   共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------
